package cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign;

import cn.maihe.elg.operation.model.dto.InnerBaseResp;
import cn.maihe.elg.operation.supports.electronicseal.kt.KtConfig;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.dto.KtCloudCommonRespDTO;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.enums.CloudSignInterfaceEnum;
import cn.maihe.elg.operation.utils.IdGenerate;
import cn.maihe.elg.operation.utils.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import com.kinsec.pdfjar.ContractSignInstance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author WJH
 * @Date 2021/08/13
 */
@Slf4j
public abstract class BaseKtCloudSignService<REQ, RESP> {

    protected Class<REQ> reqClass;
    protected Class<RESP> respClass;

    private KtConfig.CloudSign cloudSign;

    private ContractSignInstance csiRegist;
    private ContractSignInstance csiContract;
    private ContractSignInstance csiUpload;

    public BaseKtCloudSignService(KtConfig ktConfig) {

        ParameterizedType pType = (ParameterizedType) this.getClass().getGenericSuperclass();
        this.reqClass = (Class<REQ>) pType.getActualTypeArguments()[0];
        this.respClass = (Class<RESP>) pType.getActualTypeArguments()[1];

        csiRegist = new ContractSignInstance();
        csiContract = new ContractSignInstance();
        csiUpload = new ContractSignInstance();
        KtConfig.CloudSign cloudSign = ktConfig.getCloudSign();
        this.cloudSign = cloudSign;
        Assert.notNull(cloudSign, "参数[cn.maihe.elg.kt.cloud-sign]凯特pdf云签信息未配置");

        /**
         * pdf云签，投保人制证制章以及签章服务 发证平台
         */
        csiRegist.initPost(cloudSign.getRegistServer(), cloudSign.getAppId(), cloudSign.getKey());
        /**
         * 电子签约服务平台
         */
        csiContract.initPost(cloudSign.getContractServer(), cloudSign.getAppId(), cloudSign.getKey());
        csiUpload.initUpload(cloudSign.getContractServer(), cloudSign.getAppId(), cloudSign.getSecret());
    }


    /**
     * 接口方法枚举
     */
    protected abstract CloudSignInterfaceEnum serviceName();


    private ContractSignInstance getSignInstanceByEnum(CloudSignInterfaceEnum reqEnum) {
        if (reqEnum == CloudSignInterfaceEnum.contractUpload) {
            return csiUpload;
        }
        if (reqEnum.name().startsWith("contract")
                ||reqEnum==CloudSignInterfaceEnum.listByMobile
                ||reqEnum==CloudSignInterfaceEnum.getAuthSMS
                ||reqEnum==CloudSignInterfaceEnum.getAuthToken
                ||reqEnum==CloudSignInterfaceEnum.stamp
        ) {
            return csiContract;
        } else {
            return csiRegist;
        }
    }

    private String fmtReqUrl(ContractSignInstance signInstance, CloudSignInterfaceEnum reqEnum) {
        String baseUrl = null;
        if (signInstance == csiUpload || signInstance == csiContract) {
            baseUrl = this.cloudSign.getContractServer();
        } else {
            baseUrl = this.cloudSign.getContractServer();
        }
        return String.format("%s%s", baseUrl, reqEnum.getUrl());
    }


    public RESP doService(String traceId, REQ req) {
        InnerBaseResp innerBaseResp = this.doRequest(traceId, req);
        RESP resp = this.parseInnerBaseResp(innerBaseResp);
        return resp;
    }

    /**
     * 默认成功响应码，子类可覆盖重写
     */
    public boolean successCode(String code) {
        return "0000".equals(code);
    }

    public InnerBaseResp<?> doRequest(String traceId, REQ req) {
        CloudSignInterfaceEnum reqEnum = this.serviceName();
        ContractSignInstance signInstance = this.getSignInstanceByEnum(reqEnum);
        String reqJson = JSON.toJSONString(req);
        log.info("{} >> 请求接口:[{}],请求地址:{}", traceId, reqEnum.getDesc(), this.fmtReqUrl(signInstance, reqEnum));
        log.info("{} >> 请求参数：{}", traceId, reqJson);

        Map<String, String> reqMap = JSON.parseObject(reqJson, new TypeReference<Map<String, String>>() {
        });
        try {
            String respJson;
            byte[] downloadFileBy = null;
            if (reqEnum == CloudSignInterfaceEnum.contractUpload) {
                List<String> fileKeyList = Lists.newArrayList();
                List<String> filePathList = Lists.newArrayList();
                //k,v k作为文件id上传同时也作为推送签署数据及下载文件的，v作为本地上传文件绝对路径
                reqMap.entrySet().forEach(entry -> {
                    fileKeyList.add(entry.getKey());
                    filePathList.add(entry.getValue());
                });
                respJson = csiUpload.upload(filePathList, fileKeyList);
            } else if (reqEnum == CloudSignInterfaceEnum.contractDown) {
                byte[][] bufByte = new byte[1][];
                respJson = csiContract.down(reqEnum.getUrl(), reqMap, bufByte);
                downloadFileBy = (bufByte[0] != null && bufByte[0].length > 0) ? bufByte[0] : null;
            } else {
                respJson = signInstance.post(reqEnum.getUrl(), reqMap);
            }
            log.info("{} << 响应报文：{}", traceId, respJson);
            KtCloudCommonRespDTO respDTO = JSON.parseObject(respJson, KtCloudCommonRespDTO.class);
            if (respDTO == null) {
                return InnerBaseResp.exception("云签响应报文为空");
            }
            String code = respDTO.getCode();
            String msg = respDTO.getMsg();
            if (!this.successCode(code)) {
                return InnerBaseResp.exception(String.format("云签服务响应失败[%s:%s]", respDTO.getCode(), respDTO.getMsg()));
            }

            //下载文件时特殊处理
            if (reqEnum == CloudSignInterfaceEnum.contractDown) {
                return InnerBaseResp.success(downloadFileBy, msg);
            } else {
                String info = respDTO.getInfo();
                Object respData = this.parseInfo(info);
                return InnerBaseResp.success(respData, msg).setOrigCode(code);
            }

        } catch (Exception e) {
            log.error("{} << 响应异常：{}", traceId, e.getMessage(), e);
            return InnerBaseResp.exception(String.format("云签异常:%s", e.getMessage()));
        }
    }

    private Object parseInfo(String info) {
        if (StringUtil.isNotBlank(info)) {
            if (JSON.isValidObject(info)) {
                RESP resp = JSON.parseObject(info, this.respClass);
                return resp;
            } else if (JSON.isValidArray(info)) {
                List<RESP> respList = JSON.parseArray(info, this.respClass);
                return respList;
            } else {
                throw new RuntimeException("响应info业务数据不合法，暂无法处理");
            }
        } else {
            return "该接口无响应info业务数据";
        }
    }


    public REQ toReq(Object obj) {
        return JSON.parseObject(JSON.toJSONBytes(obj), this.reqClass);
    }

    protected RESP parseInnerBaseResp(InnerBaseResp<?> innerBaseResp) {
        if (innerBaseResp.getRespCode().isSuccess()) {
            try {
                Object respData = innerBaseResp.getData();
                //如果是集合实例则默认返回第一个对象，要全部返回则使用 doRequest原始转换获取
                RESP resp = null;
                if (respData instanceof Collection) {
                    Collection collection = (Collection)respData;
                    if(CollectionUtils.isEmpty(collection)){
                        resp  = null;
                    }else {
                        resp = (RESP) collection.iterator().next();
                    }
                } else {
                    resp = (RESP) innerBaseResp.getData();
                }
                return resp;
            } catch (Exception e) {
                throw new RuntimeException(this.serviceName().getDesc() + ":解析响应报文异常:" + e.getMessage(), e);
            }
        }
        throw new RuntimeException(this.serviceName().getDesc() + ":" + innerBaseResp.getMessage());
    }

    @Profile({"dev", "test"})
    public CloudSignInterfaceEnum getServiceName() {
        return this.serviceName();
    }

    @Profile({"dev", "test"})
    public InnerBaseResp<?> doTestRequest(REQ req) {
        String traceId = IdGenerate.genTranceId();
        return this.doRequest(traceId, req);
    }
}
